#include "SG90_Config.h"

/**
    ******************************************************************************
    * @brief  SG90舵机PWM初始化
    * @param  无
    * @retval 无
    * @detail 初始化TIM3通道2输出PWM信号，配置如下：
    * - PWM频率: 50Hz (周期20ms)
    * - 脉冲宽度范围: 0.5ms-2.5ms (对应0°-180°)
    * - 使用PB5引脚(TIM3_CH2)
    * @detail 硬件配置流程：
 	*         1. 使能TIM3和GPIOB时钟
 	*         2. 配置PB5为复用推挽输出
 	*         3. 配置TIM3时基单元（默认50Hz频率）
 	*         4. 配置PWM输出模式
 	*         5. 使能定时器
 	* @note   引脚重映射需根据实际硬件调整
    ******************************************************************************
*/
void SG90_Config(void)
{
    /* 1. 使能TIM3时钟（APB1总线） */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    /* 说明：
       - TIM3挂载在APB1总线（最大36MHz，但时钟树配置后实际为72MHz）
       - 必须使能时钟才能访问外设寄存器 */

    /* 2. 使能GPIOB和AFIO时钟（APB2总线） */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
    /* 说明：
       - GPIOB用于PWM输出引脚
       - AFIO用于引脚重映射功能 */

    /* 3. 配置TIM3通道2部分重映射到PB5 */
    GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);
    /* 说明：
       - 默认TIM3_CH2在PA7，重映射后改为PB5
       - 需确认硬件连接与重映射配置匹配 */

    /* 4. 配置PB5为复用推挽输出 */
    GPIO_InitTypeDef GPIO_InitStructure = {
        .GPIO_Mode  = GPIO_Mode_AF_PP,  /* 复用推挽输出（PWM信号必须配置为此模式） */
        .GPIO_Pin   = GPIO_Pin_5,       /* 选择PB5引脚 */
        .GPIO_Speed = GPIO_Speed_50MHz, /* 高速模式确保信号边沿质量 */
    };
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    /* 说明：
       - 推挽输出提供足够的驱动能力
       - 50MHz速度适应PWM高频切换 */
}

/**
  ******************************************************************************
  * @brief  设置PWM频率和占空比
  * @param  Freq: PWM信号频率（单位：Hz）
  * @param  Duty: 脉冲宽度值（5=0.5ms, 25=2.5ms）
  * @retval 无
  * @detail 计算公式：
  *         - 定时器时钟 = 72MHz / (PSC + 1)
  *         - 周期 = (ARR + 1) / 定时器时钟
  *         - 占空比 = CCR / (ARR + 1)
  * @note   修改参数时会先关闭定时器，配置完成后自动重启
  ******************************************************************************
  */
void SG90_Set_Freq_Duty(u32 Freq, u32 Duty)
{
	/* 安全措施：修改参数前关闭定时器 */
    TIM_Cmd(TIM3, DISABLE);

	/*
	 * 设置频率
	 *
     * 配置 TIM3 时基单元 
     * 舵机控制信号要求20ms周期(50Hz) => 1/0.02 = 50
	 * 72MHz / 7200 -> 10kHz / 50Hz -> 200
	 * 
	 * @note：	
	 * 		Period 为计数次数，代表着一个周期需要数多少次，
	 * 		每个计数100us（0.1ms），200*100us=20ms，
	 * 		代表着占空比为1时  --> 20ms里面为0.1ms
	 * 			  占空比为5时  --> 20ms里面为0.5ms
	 * 			  占空比为25时 --> 20ms里面为2.5ms
	 */
    /* 配置TIM3时基单元 */
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure = {
        .TIM_Prescaler     = 7200 - 1,         		/* 预分频值：72MHz/7200=10kHz */
        .TIM_Period        = (200 / Freq) - 1, 		/* 自动重装载值：10kHz/50Hz=200-1 */
        .TIM_ClockDivision = 0,                		/* 无时钟分频 */
        .TIM_CounterMode   = TIM_CounterMode_Up 	/* 向上计数模式 */
    };
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

	/* 配置PWM模式（通道2） */
    TIM_OCInitTypeDef TIM_OCInitStructure = {
        .TIM_OCMode      = TIM_OCMode_PWM1,      	/* PWM模式1：CNT<CCR时输出有效电平 */
        .TIM_OutputState = TIM_OutputState_Enable, 	/* 使能输出 */
        .TIM_OCPolarity  = TIM_OCPolarity_High,  	/* 高电平为有效电平 */
        .TIM_Pulse       = Duty                 	/* 初始占空比 */
    };
    TIM_OC2Init(TIM3, &TIM_OCInitStructure);
    /* 说明：
       - PWM模式1：CNT递增时，CNT<CCR输出有效电平
       - 高电平有效符合SG90控制信号规范 */

     /* 使能预装载寄存器（避免参数更新时产生毛刺） */
    TIM_ARRPreloadConfig(TIM3, ENABLE);            		/* 使能ARR缓冲 */
    TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);	/* 使能CCR2缓冲 */

    /* 重新启动定时器 */
    TIM_Cmd(TIM3, ENABLE);
}

/**
  ******************************************************************************
  * @brief  舵机旋转测试（0°→180°往复运动）
  * @param  无
  * @retval 无
  * @detail 运动流程：
  *         1. 从0.5ms(0°)逐步增加到2.5ms(180°)
  *         2. 到达极限位置后反向运动
  *         3. 每个位置停留500ms
  * @note   此函数为演示代码，实际应用建议使用状态机实现
  ******************************************************************************
  */
void SG90_Turn(void)
{
    /* 初始化位置：0°（0.5ms脉冲） */
    SG90_Set_Freq_Duty(1, 5);

	/* 正向运动：0°→180°（每步增加0.5ms） */
	for(u32 i = 1; i <= 5; i++) {
		TIM3->CCR2 = 5 * i;  /* 5=0.5ms, 10=1ms...25=2.5ms */
		Delay_ms(500);       /* 保持位置500ms */
	}

	/* 反向运动：180°→0°（每步减少0.5ms） */
	for(u32 i = 4; i >= 1; i--) {
		TIM3->CCR2 = 5 * i;
		Delay_ms(500);
	}

}
